// main.js - Electron 主进程

const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path');
const net = require('net');
const WebSocket = require('ws');
const protobuf = require('protobufjs');
const fs = require('fs');
const fsPromises = require('fs').promises;

// 导入协议编解码器函数
// 导入整个 protocol-codec 模块
const protocolCodec = require('./protocol-codec');
const { registerProtocol, unregisterProtocol, clearProtocolRegistry } = protocolCodec;

// 业务脚本API对象
const businessScriptAPI = {
    sendData: (data) => {
        if (socket) {
            handleSendData(data);
        } else {
            logToRenderer('未连接，无法发送数据', 'error');
        }
    },
    log: logToRenderer,
    setTimeout: (callback, delay) => setTimeout(callback, delay),
    setInterval: (callback, interval) => setInterval(callback, interval),
    clearTimeout: (timeoutId) => clearTimeout(timeoutId),
    clearInterval: (intervalId) => clearInterval(intervalId),
    codec: {
        registerProtocol,
        unregisterProtocol,
        clearProtocolRegistry
    }
};

// 在数据接收处理中，确保使用同一个 protocol-codec 实例
// 将 codec.unpack 替换为 protocolCodec.unpack

let mainWindow;
let socket = null; // 用于存储 TCP 或 WebSocket 连接实例
let protoRoot = null; // 用于存储加载的 .proto 文件解析结果
let currentProtoDir = ''; // 当前选择的 proto 目录
let codec = null; // 用于存储自定义编解码器或协议处理器
let tcpBuffer = Buffer.alloc(0); // 用于处理 TCP 粘包
// 添加全局变量来存储当前选择的接收消息类型
let currentReceiveMessageType = null;

// 业务脚本相关变量
let businessScript = null; // 当前加载的业务脚本实例
let currentBusinessScriptDir = ''; // 当前业务脚本目录

// 创建窗口函数
function createWindow() {
    mainWindow = new BrowserWindow({
        width: 1200,
        height: 800,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            preload: path.join(__dirname, 'preload.js')
        }
    });

    mainWindow.loadFile('index.html');
    mainWindow.webContents.openDevTools();
}

// 向渲染进程发送日志消息
function logToRenderer(message, type = 'info') {
    if (mainWindow && !mainWindow.isDestroyed()) {
        mainWindow.webContents.send('log', { message, type });
    }
}

// 向渲染进程发送连接状态
function statusToRenderer(status, message) {
    if (mainWindow && !mainWindow.isDestroyed()) {
        mainWindow.webContents.send('connection-status', { status, message });
    }
}

// 向渲染进程发送接收到的数据
function dataToRenderer(data, protocol) {
    console.log('[Debug] dataToRenderer 被调用，参数:', { data, protocol });
    if (mainWindow && !mainWindow.isDestroyed()) {
        console.log('[Debug] 主窗口存在且未销毁，发送事件到渲染器');
        mainWindow.webContents.send('data-received', data, protocol);
        console.log('[Debug] 事件已发送到渲染器');
    } else {
        console.log('[Debug] 主窗口不存在或已销毁');
    }
}

// 删除这里重复的 businessScriptAPI 定义（第87-102行）
// const businessScriptAPI = { ... }  <-- 删除这整个重复的对象定义

// 提取发送数据逻辑为独立函数
function handleSendData(data) {
    if (!socket || (socket instanceof WebSocket && socket.readyState !== WebSocket.OPEN) || (socket instanceof net.Socket && !socket.writable)) {
        logToRenderer('未连接或连接不可写，无法发送数据。', 'error');
        return;
    }
    
    try {
        logToRenderer(`${data.protocol} 序列化方式`, 'info');
        let buffer;
        if (data.protocol === 'custom' || data.protocol === 'protobuf') {
            if (!protocolCodec || typeof protocolCodec.pack !== 'function') {
                throw new Error('未加载有效的协议处理器 (需要 pack 方法)。');
            }
            const packData = {
                protocol: data.protocol,
                payload: data.data ? data.data.payload : data.payload,
                messageType: data.data ? data.data.messageType : data.messageType,
                protocolId: data.data ? data.data.protocolId : data.protocolId,
                rawData: data,
            };
            buffer = protocolCodec.pack(packData, protoRoot, logToRenderer);
            logToRenderer(`发送 [${data.protocol}] 数据，使用自定义协议封包`, 'send');
        } else {
            throw new Error('不支持的协议类型');
         //   buffer = Buffer.from(data.data || data);
          //  logToRenderer(`发送 [默认]: ${data.data || data}`, 'send');
        }
        socket.write(buffer);
    } catch(e) {
        logToRenderer(`发送失败: ${e.message}`, 'error');
    }
}

app.whenReady().then(() => {
    createWindow();

    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow();
        }
    });
});

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// --- IPC 事件监听 ---

// 添加IPC处理器来设置接收消息类型（移到全局范围）
ipcMain.handle('set-receive-message-type', async (event, messageType) => {
    currentReceiveMessageType = messageType;
    logToRenderer(`设置接收消息类型为: ${messageType}`, 'info');
    return { success: true };
});

// 修改原有的send-data处理器
ipcMain.on('send-data', (event, data) => {
    // 如果data是字符串，尝试解析为JSON对象
    let parsedData;
    if (typeof data === 'string') {
        try {
            parsedData = JSON.parse(data);
        } catch (e) {
            logToRenderer(`JSON解析失败: ${e.message}`, 'error');
            return;
        }
    } else {
        parsedData = data;
    }
    handleSendData(parsedData);
});

// 删除原有的业务脚本目录相关的IPC处理器
// 将 load-business-script-dir 和 load-business-script 替换为单个处理器

// 添加业务脚本相关的IPC处理器 - 直接选择JS文件
ipcMain.handle('load-business-script-file', async () => {
    try {
        const result = await dialog.showOpenDialog(mainWindow, {
            title: '请选择业务脚本文件',
            filters: [{ name: 'JavaScript Files', extensions: ['js'] }],
            properties: ['openFile']
        });

        if (result.canceled || result.filePaths.length === 0) {
            logToRenderer('用户取消选择业务脚本文件', 'info');
            return { success: false, message: '用户取消选择' };
        }

        const scriptPath = result.filePaths[0];
        const scriptDir = path.dirname(scriptPath);
        const templatePath = path.join(scriptDir, 'business-script-template.js');
        
        // 修复: 清除业务脚本和其父类模板的模块缓存
        delete require.cache[require.resolve(scriptPath)];
        if (fs.existsSync(templatePath)) {
            delete require.cache[require.resolve(templatePath)];
        }
        
        // 停止当前业务脚本
        if (businessScript && typeof businessScript.onDisconnected === 'function') {
            try {
                await businessScript.onDisconnected();
            } catch (error) {
                logToRenderer(`停止旧业务脚本失败: ${error.message}`, 'error');
            }
            businessScript = null;
        }

        // 加载新的业务脚本
        const ScriptClass = require(scriptPath);
        businessScript = new ScriptClass(businessScriptAPI);

        const fileName = path.basename(scriptPath);
        logToRenderer(`成功加载业务脚本: ${fileName}`, 'success');
        return { success: true, fileName, filePath: scriptPath };
    } catch (error) {
        logToRenderer(`加载业务脚本失败: ${error.message}`, 'error');
        businessScript = null;
        return { success: false, message: error.message };
    }
});

// 处理连接请求
ipcMain.on('connect', (event, address) => {
    if (socket) {
        logToRenderer('已有连接存在，请先断开。', 'error');
        return;
    }

    try {
        if (address.startsWith('ws://') || address.startsWith('wss://')) {
            // WebSocket 连接
            socket = new WebSocket(address);
            logToRenderer(`正在连接到 WebSocket: ${address}`, 'info');

            socket.on('open', async () => {
                statusToRenderer('connected', `已连接到 ${address}`);
                
                // 调用业务脚本的连接成功回调
                if (businessScript && typeof businessScript.onConnected === 'function') {
                    try {
                        await businessScript.onConnected({ address, protocol: 'websocket' });
                    } catch (error) {
                        logToRenderer(`业务脚本连接回调失败: ${error.message}`, 'error');
                    }
                }
            });

            socket.on('message', async (data) => {
                // WebSocket 消息也可能需要解包
                if (protocolCodec && typeof protocolCodec.unpack === 'function') {
                    try {
                        // WebSocket 通常是消息边界清晰的，所以直接传递 data
                        protocolCodec.unpack(data, async (unpackedData) => {
                            logToRenderer('接收到 WebSocket 数据并使用自定义协议解包', 'receive');
                            // 假设解包后的数据可以直接发给渲染器
                            dataToRenderer(unpackedData, 'ws-custom');
                            
                            // 调用业务脚本的数据接收回调
                            if (businessScript && typeof businessScript.onDataReceived === 'function') {
                                try {
                                    await businessScript.onDataReceived(unpackedData, 'ws-custom');
                                } catch (error) {
                                    logToRenderer(`业务脚本数据处理失败: ${error.message}`, 'error');
                                }
                            }
                        });
                    } catch (e) {
                        logToRenderer(`自定义协议解包失败: ${e.message}`, 'error');
                        dataToRenderer(data, 'ws'); // 解包失败，发送原始数据
                    }
                } else {
                    dataToRenderer(data, 'ws');
                }
            });

            socket.on('close', async () => {
                statusToRenderer('disconnected', 'WebSocket 连接已断开');
                socket = null;
                
                // 调用业务脚本的断开连接回调
                if (businessScript && typeof businessScript.onDisconnected === 'function') {
                    try {
                        await businessScript.onDisconnected();
                    } catch (error) {
                        logToRenderer(`业务脚本断开回调失败: ${error.message}`, 'error');
                    }
                }
            });

            socket.on('error', async (err) => {
                statusToRenderer('error', `WebSocket 错误: ${err.message}`);
                socket = null;
                
                // 调用业务脚本的错误回调
                if (businessScript && typeof businessScript.onError === 'function') {
                    try {
                        await businessScript.onError(err);
                    } catch (error) {
                        logToRenderer(`业务脚本错误回调失败: ${error.message}`, 'error');
                    }
                }
            });

        } else if (address.startsWith('tcp://')) {
            // TCP 连接
            const addrParts = address.substring(6).split(':');
            const host = addrParts[0];
            const port = parseInt(addrParts[1], 10);

            if (!host || isNaN(port)) {
                throw new Error('无效的 TCP 地址格式。应为 tcp://host:port');
            }

            socket = new net.Socket();
            logToRenderer(`正在连接到 TCP: ${host}:${port}`, 'info');

            socket.connect(port, host, async () => {
                statusToRenderer('connected', `已连接到 ${host}:${port}`);
                
                // 调用业务脚本的连接成功回调
                if (businessScript && typeof businessScript.onConnected === 'function') {
                    try {
                        await businessScript.onConnected({ host, port, protocol: 'tcp' });
                    } catch (error) {
                        logToRenderer(`业务脚本连接回调失败: ${error.message}`, 'error');
                    }
                }
            });

            socket.on('data', async (data) => {
                tcpBuffer = Buffer.concat([tcpBuffer, data]);
                // 如果加载了协议处理器，则使用它来解包
                if (protocolCodec && typeof protocolCodec.unpack === 'function') {
                    try {
                        tcpBuffer = protocolCodec.unpack(tcpBuffer, async (unpackedData) => {
                            logToRenderer(`接收到数据并使用自定义协议解包`, 'receive');
                            // unpackedData 现在应该包含解码后的protobuf字段和值
                            dataToRenderer(unpackedData, 'protobuf-tcp');
                            
                            // 调用业务脚本的数据接收回调
                            if (businessScript && typeof businessScript.onDataReceived === 'function') {
                                try {
                                    await businessScript.onDataReceived(unpackedData, 'protobuf-tcp');
                                } catch (error) {
                                    logToRenderer(`业务脚本数据处理失败: ${error.message}`, 'error');
                                }
                            }
                        }, protoRoot, logToRenderer, currentReceiveMessageType); // 传入用户选择的接收消息类型
                    } catch (e) {
                        logToRenderer(`自定义协议解包失败: ${e.message}`, 'error');
                        // 出错时，为避免死循环，清空缓冲区
                        tcpBuffer = Buffer.alloc(0);
                        
                        // 调用业务脚本的错误回调
                        if (businessScript && typeof businessScript.onError === 'function') {
                            try {
                                await businessScript.onError(e);
                            } catch (error) {
                                logToRenderer(`业务脚本错误回调失败: ${error.message}`, 'error');
                            }
                        }
                    }
                } else {
                    // 默认或无处理器的行为，可以保留或移除
                    logToRenderer('未加载协议处理器，接收到原始TCP数据', 'receive');
                    dataToRenderer(data, 'tcp');
                    tcpBuffer = Buffer.alloc(0); // 清空，因为不知道如何处理
                }
            });

            socket.on('close', async () => {
                statusToRenderer('disconnected', 'TCP 连接已断开');
                socket = null;
                
                // 调用业务脚本的断开连接回调
                if (businessScript && typeof businessScript.onDisconnected === 'function') {
                    try {
                        await businessScript.onDisconnected();
                    } catch (error) {
                        logToRenderer(`业务脚本断开回调失败: ${error.message}`, 'error');
                    }
                }
            });

            socket.on('error', async (err) => {
                statusToRenderer('error', `TCP 错误: ${err.message}`);
                socket = null;
                
                // 调用业务脚本的错误回调
                if (businessScript && typeof businessScript.onError === 'function') {
                    try {
                        await businessScript.onError(err);
                    } catch (error) {
                        logToRenderer(`业务脚本错误回调失败: ${error.message}`, 'error');
                    }
                }
            });

        } else {
            throw new Error('地址格式无效，请使用 ws:// 或 tcp:// 前缀');
        }
    } catch (error) {
        statusToRenderer('error', error.message);
        socket = null;
    }
});

// 处理断开连接请求
ipcMain.on('disconnect', async () => {
    if (socket) {
        if (socket instanceof WebSocket) {
            socket.close();
        } else if (socket instanceof net.Socket) {
            socket.destroy();
        }
        socket = null;
        codec = null; // 断开连接时重置编解码器
        logToRenderer('连接已手动断开。', 'info');
        
        // 调用业务脚本的断开连接回调
        if (businessScript && typeof businessScript.onDisconnected === 'function') {
            try {
                await businessScript.onDisconnected();
            } catch (error) {
                logToRenderer(`业务脚本断开回调失败: ${error.message}`, 'error');
            }
        }
    }
});

// --- .proto 文件加载相关 --- 

// 添加原始的 load-proto-dir 处理器
ipcMain.handle('load-proto-dir', async () => {
    try {
        const result = await dialog.showOpenDialog(mainWindow, {
            title: '请选择包含 .proto 文件的目录',
            properties: ['openDirectory']
        });

        if (result.canceled || result.filePaths.length === 0) {
            logToRenderer('用户取消选择目录', 'info');
            return { success: false, message: '用户取消选择' };
        }

        const dirPath = result.filePaths[0];
        currentProtoDir = dirPath;
        
        // 加载目录中的所有 .proto 文件
        const files = fs.readdirSync(dirPath).filter(file => file.endsWith('.proto'));
        
        if (files.length === 0) {
            return { success: false, message: '目录中没有找到 .proto 文件' };
        }

        // 加载所有 .proto 文件到 protoRoot
        protoRoot = new protobuf.Root();
        for (const file of files) {
            await protoRoot.load(path.join(dirPath, file), { keepCase: true });
        }
        
        logToRenderer(`成功加载 proto 目录: ${dirPath}`, 'success');
        return { success: true, path: dirPath, files };
    } catch (error) {
        logToRenderer(`加载 proto 目录失败: ${error.message}`, 'error');
        return { success: false, message: error.message };
    }
});

// 添加原始的 load-codec-script 处理器
ipcMain.handle('load-codec-script', async () => {
    try {
        const result = await dialog.showOpenDialog(mainWindow, {
            title: '请选择编解码脚本文件',
            filters: [{ name: 'JavaScript Files', extensions: ['js'] }],
            properties: ['openFile']
        });

        if (result.canceled || result.filePaths.length === 0) {
            logToRenderer('用户取消选择编解码脚本', 'info');
            return { success: false, message: '用户取消选择' };
        }

        const scriptPath = result.filePaths[0];
        
        // 清除模块缓存
        delete require.cache[require.resolve(scriptPath)];
        
        // 加载编解码脚本
        const loadedCodec = require(scriptPath);
        
        // 验证脚本是否包含必要的方法
        if (typeof loadedCodec.pack === 'function' && typeof loadedCodec.unpack === 'function') {
            codec = loadedCodec;
            logToRenderer(`成功加载编解码脚本: ${path.basename(scriptPath)}`, 'success');
            return { success: true, filePath: scriptPath };
        } else {
            return { success: false, message: '脚本必须导出 pack 和 unpack 函数' };
        }
    } catch (error) {
        logToRenderer(`加载编解码脚本失败: ${error.message}`, 'error');
        return { success: false, message: error.message };
    }
});

ipcMain.handle('auto-load-proto-dir', async (event, dirPath) => {
    try {
        if (!fs.existsSync(dirPath)) {
            return { success: false, message: '目录不存在' };
        }
        
        currentProtoDir = dirPath;
        const files = fs.readdirSync(dirPath).filter(file => file.endsWith('.proto'));
        
        if (files.length === 0) {
            return { success: false, message: '目录中没有找到 .proto 文件' };
        }
        
        // 重要：加载所有 .proto 文件到 protoRoot
        protoRoot = new protobuf.Root();
        for (const file of files) {
            await protoRoot.load(path.join(dirPath, file), { keepCase: true });
        }
        
        return { success: true, path: dirPath, files };
    } catch (error) {
        return { success: false, message: error.message };
    }
});

// 自动加载编解码脚本
ipcMain.handle('auto-load-codec-script', async (event, scriptPath) => {
    try {
        if (!fs.existsSync(scriptPath)) {
            return { success: false, message: '脚本文件不存在' };
        }
        
        delete require.cache[require.resolve(scriptPath)];
        const loadedCodec = require(scriptPath);
        
        if (typeof loadedCodec.pack === 'function' && typeof loadedCodec.unpack === 'function') {
            codec = loadedCodec;
            return { success: true, filePath: scriptPath };
        } else {
            return { success: false, message: '脚本必须导出 pack 和 unpack 函数' };
        }
    } catch (error) {
        return { success: false, message: error.message };
    }
});

// 自动加载业务脚本
ipcMain.handle('auto-load-business-script', async (event, scriptPath) => {
    try {
        if (!fs.existsSync(scriptPath)) {
            return { success: false, message: '脚本文件不存在' };
        }
        
        const scriptDir = path.dirname(scriptPath);
        const templatePath = path.join(scriptDir, 'business-script-template.js');
        
        delete require.cache[require.resolve(scriptPath)];
        if (fs.existsSync(templatePath)) {
            delete require.cache[require.resolve(templatePath)];
        }
        
        const ScriptClass = require(scriptPath);
        businessScript = new ScriptClass(businessScriptAPI);
        
        const fileName = path.basename(scriptPath);
        return { success: true, fileName, filePath: scriptPath };
    } catch (error) {
        return { success: false, message: error.message };
    }
});

ipcMain.handle('load-proto-file', async (event, fileName) => {
    if (!currentProtoDir || !protoRoot) {
        return { success: false, message: '请先加载 .proto 目录' };
    }
    try {
        // 重新加载以获取特定文件的类型
        const tempRoot = new protobuf.Root();
        await tempRoot.load(path.join(currentProtoDir, fileName), { keepCase: true });
        const types = [];
        // 递归查找所有消息类型
        function findMessageTypes(namespace) {
            if (!namespace) return;
            namespace.nestedArray.forEach(nested => {
                if (nested instanceof protobuf.Type) {
                    types.push(nested.fullName.startsWith('.') ? nested.fullName.substring(1) : nested.fullName);
                }
                if (nested instanceof protobuf.Namespace) {
                    findMessageTypes(nested);
                }
            });
        }
        findMessageTypes(tempRoot);
        return { success: true, types };
    } catch (error) {
        logToRenderer(`解析 ${fileName} 失败: ${error.message}`, 'error');
        return { success: false, message: error.message };
    }
});

ipcMain.handle('get-message-details', async (event, messageType) => {
    if (!protoRoot) {
        return { success: false, message: 'Proto 根未加载' };
    }
    try {
        const type = protoRoot.lookupType(messageType);
        const fields = type.fieldsArray.map(field => {
            let isMessageType = false;
            try {
                // 尝试查找类型，如果不是消息类型（如 int32, string），会抛出错误
                isMessageType = protoRoot.lookupType(field.type) instanceof protobuf.Type;
            } catch (e) {
                // 忽略错误，isMessageType 保持 false
            }
            return {
                name: field.name,
                type: field.type,
                id: field.id,
                isRepeated: field.repeated,
                isMessageType: isMessageType
            };
        });
        return { success: true, fields };
    } catch (error) {
        return { success: false, message: error.message };
    }
});

ipcMain.handle('decode-proto', async (event, { messageType, data }) => {
    if (!protoRoot) {
        return { success: false, error: 'Proto 根未加载' };
    }
    try {
        const type = protoRoot.lookupType(messageType);
        const message = type.decode(data);
        const pojo = type.toObject(message, { defaults: true });
        return { success: true, data: pojo };
    } catch (error) {
        return { success: false, error: error.message };
    }
});
